﻿using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
using System;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using zijian666.WebApi.Attributes;

namespace zijian666.WebApi.SwaggerOptions
{
    class SwaggerFormatResultFilter : IOperationFilter
    {
        private readonly IResultFormatter? _resultFormatter;

        public SwaggerFormatResultFilter(IServiceProvider services) => _resultFormatter = services?.GetService<IResultFormatter>();

        /// <summary>
        /// 应用过滤器
        /// </summary>
        /// <param name="operation"></param>
        /// <param name="context"></param>
        public void Apply(OpenApiOperation operation, OperationFilterContext context)
        {
            if (_resultFormatter is null || context is null || operation is null)
            {
                return;
            }

            if (context.MethodInfo.IsDefined(typeof(DontFormatResultAttribute)))
            {
                return;
            }

            var type = context.ApiDescription.SupportedResponseTypes.FirstOrDefault()?.Type ?? context.MethodInfo.ReturnType;
            if (type is null || type == typeof(void))
            {
                type = typeof(object);
            }
            else if (type.IsAssignableTo(typeof(Task)) && type.IsGenericType)
            {
                type = type.GetGenericArguments()[0];
            }

            if (type.IsValueType && Nullable.GetUnderlyingType(type) is Type t)
            {
                type = t;
            }
            t = _resultFormatter.GetResultType(type ?? typeof(object));
            if (t == type)
            {
                return;
            }
            type = t;

            if (operation.Responses.TryGetValue("200", out var response))
            {
                if (response.Content.Count == 0)
                {
                    response.Content.Add("application/json", new OpenApiMediaType() { Schema = GetSchema(context, t) });
                }
                else
                {
                    response.Content.Keys.Where(x => !x.StartsWith("application/json", StringComparison.OrdinalIgnoreCase)).ToList().ForEach(x => response.Content.Remove(x));
                    if (context.MethodInfo.ReturnType.IsAssignableToOneOf(typeof(IActionResult)))
                    {
                        return;
                    }
                    foreach (var item in response.Content.Values)
                    {
                        item.Schema = GetSchema(context, t);
                    }
                }
            }
        }

        public static OpenApiSchema GetSchema(OperationFilterContext context, Type type)
        {
            if (context is null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            if (type is null)
            {
                throw new ArgumentNullException(nameof(type));
            }

            return context.SchemaRepository.TryLookupByType(type, out var schema)
          ? schema
          : context.SchemaGenerator.GenerateSchema(type, context.SchemaRepository);
        }

        //public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
        //{
        //    if (_context.Completed)
        //    {
        //        return;
        //    }
        //    lock (_context)
        //    {
        //        foreach (var operation in swaggerDoc.Paths.SelectMany(x => x.Value.Operations).Select(x => x.Value))
        //        {
        //            if (_context.Mappings.TryGetValue(operation.OperationId, out var resultType))
        //            {

        //                foreach (var key in operation.Responses.Keys)
        //                {
        //                    if (!context.SchemaRepository.TryLookupByType(resultType, out var resultSchema))
        //                    {
        //                        resultSchema = context.SchemaGenerator.GenerateSchema(resultType, context.SchemaRepository);
        //                    }
        //                    var apiResponse = operation.Responses[key];
        //                    if (apiResponse.Content.Count == 0)
        //                    {
        //                        var mediaType = new OpenApiMediaType() {
        //                            Schema = new OpenApiSchema { Reference = resultSchema.Reference }
        //                        };
        //                        apiResponse.Content.Add("application/json", mediaType);
        //                        apiResponse.Content.Add("text/json", mediaType);
        //                        apiResponse.Content.Add("text/plain", mediaType);
        //                    }
        //                    else
        //                    {
        //                        foreach (var item in apiResponse.Content.Values)
        //                        {
        //                            item.Schema.Reference = resultSchema.Reference;
        //                            item.Schema.Type = null;
        //                        }
        //                    }
        //                }
        //            }
        //        }
        //        _context.Completed = true;
        //    }
        //}
    }
}
